#pragma once
#include <set>
#include <string>
#include <deque>

namespace CppClean
{

typedef std::set<char>	CHARSET;
typedef std::set<string> STRINGSET;
typedef std::deque<string> STRINGLIST;

class CDbgUtil
{
public:
	CDbgUtil(void){}
	~CDbgUtil(void){}

public:
	static void myDebugOutputW(LPCWSTR format, BOOL bNewLine, ...);
	static void myDebugOutputA(LPCSTR format, BOOL bNewLine, ...);
};

class CCharUtil
{
public:
	CCharUtil(){}
	~CCharUtil(){}

public:
	static void AppendSet(CHARSET& sets, const char* strings);
	static bool InSet(const char ch, CHARSET& sets);
	static void DumpSet(CHARSET& sets);
	static int findchar(const char* source, char ch, int start=0);
	static int findchar(const char* source, const char* str, int start=0);
};

//////////////////////////////////////////////////////////////////////////
class IReference
{
public:
	virtual DWORD AddRef() = 0;
	virtual DWORD ReleaseRef() = 0;

protected:
	virtual ~IReference() {}
};

class CLock
{
public:
	CLock()
	{ 
		InitializeCriticalSection(&m_cs); 
	}

	virtual ~CLock()
	{
		DeleteCriticalSection(&m_cs); 
	}

public:
	void Lock()
	{
		EnterCriticalSection(&m_cs);
	}

	void Unlock()
	{
		LeaveCriticalSection(&m_cs);
	}

protected:
	CRITICAL_SECTION m_cs;
};

class CAutoLock
{
public:
	CAutoLock(CLock* pLock)
		:m_pLock(pLock)
	{
		if(m_pLock)
			m_pLock->Lock();
	}
	virtual ~CAutoLock() 
	{
		if(m_pLock)
			m_pLock->Unlock();
	}

protected:
	CLock* m_pLock;
};

class CCpObject
{
public:
	CCpObject()
		:m_nAtomic(0)
	{
	}

	virtual ~CCpObject()
	{
	}

public:
	DWORD AddRef()
	{
		CAutoLock theLock(&m_Lock);
		return ++m_nAtomic;
	}

	DWORD ReleaseRef()
	{
		m_Lock.Lock();
		--m_nAtomic;
		m_Lock.Unlock();
		if (m_nAtomic == 0) 
			OnReferenceDestory();
		return m_nAtomic;
	}

	DWORD GetReference()
	{
		CAutoLock theLock(&m_Lock);
		return m_nAtomic;
	}

	virtual string GetName()
	{
		return "[unknown]";
	}

	virtual string GetString()
	{
		return "[Object]";
	}

protected:
	virtual void OnReferenceDestory()
	{
		delete this;
	}

	int m_nAtomic;
	CLock m_Lock;
};


/**
*	Auto pointer for ReferenceControl
*/
template <class T> class CCpAutoPtr
{
public:
	CCpAutoPtr(T *aPtr = NULL) 
		:m_pRawPtr(aPtr)
	{
		if (m_pRawPtr)
			m_pRawPtr->AddRef();
	}

	CCpAutoPtr(const CCpAutoPtr& aAutoPtr) 
		:m_pRawPtr(aAutoPtr.m_pRawPtr)
	{
		if (m_pRawPtr)
			m_pRawPtr->AddRef();
	}

	~CCpAutoPtr() 
	{
		if (m_pRawPtr)
			m_pRawPtr->ReleaseRef();
	}

	CCpAutoPtr& operator = (const CCpAutoPtr& aAutoPtr) 
	{
		*this = aAutoPtr.m_pRawPtr;

		return *this;
	}

	CCpAutoPtr& operator = (T* aPtr) 
	{
		if (m_pRawPtr == aPtr)
			return *this;

		if (aPtr)
			aPtr->AddRef();
		if (m_pRawPtr)
			m_pRawPtr->ReleaseRef();
		m_pRawPtr = aPtr;
		return *this;
	}

	operator void* () const 
	{
		return m_pRawPtr;
	}

	T* operator -> () const 
	{
		return m_pRawPtr;
	}

	T* Get() const 
	{
		return m_pRawPtr;
	}

	T* ParaIn() const 
	{
		return m_pRawPtr;
	}

	T*& ParaOut() 
	{
		if (m_pRawPtr) {
			m_pRawPtr->ReleaseRef();
			m_pRawPtr = NULL;
		}
		return static_cast<T*&>(m_pRawPtr);
	}

	T*& ParaInOut() 
	{
		return static_cast<T*&>(m_pRawPtr);
	}

	T& operator * () const 
	{
		CM_ASSERTE(m_pRawPtr);
		return *m_pRawPtr;
	}

private:
	T *m_pRawPtr;
};

typedef deque<CCpAutoPtr<CCpObject> >	OBJECTLIST;
typedef CCpAutoPtr<CCpObject>	CPOBJECT;

class CStringUtil
{
public:
	CStringUtil(){}
	~CStringUtil(){}

public:
	template<typename R>
	static void Split(R& sets, const char* strings, char sep = ' ')
	{
		if(strings == NULL)
			return;

		int nLen = strlen(strings);
		int nStart = 0;
		while(1)
		{
			const char* pNext = strchr(strings + nStart, sep);
			if(pNext == NULL)
				break;
			int nNext = pNext - strings;
			if(nNext - nStart > 0)
				sets.insert(string(strings + nStart, nNext - nStart));
			nStart = nNext + 1;
			if(nStart > nLen)
				break;
		}
		if(nStart < nLen)
		{
			string sTemp(strings + nStart, nLen - nStart);
			sTemp.erase(0, sTemp.find_first_not_of(" "));
			if(!sTemp.empty())
				sets.insert(sTemp);
		}
	}

	template<typename R>
	static string JoinObject(R& sets, LPCSTR sep, LPCSTR filler="Null")
	{
		R::iterator it = sets.begin();
		string ret("[");
		for(; it != sets.end(); it++)
		{
			CCpObject* itval = (CCpObject*)it->Get();
			if(itval && itval->GetString().length() != 0)
				ret += itval->GetString();
			else
				ret += filler;
			ret += sep;
		}
		ret.erase(ret.find_last_not_of(sep[0]) + 1, ret.length());
		ret += "]";
		return ret;
	}

	template<typename R>
	static string JoinObjectName(R& sets, LPCSTR sep, LPCSTR filler="Null")
	{
		R::iterator it = sets.begin();
		string ret;
		for(; it != sets.end(); it++)
		{
			CCpObject* itval = (CCpObject*)it->Get();
			if(itval && itval->GetName().length() != 0)
				ret += itval->GetName();
			else
				ret += filler;
			ret += sep;
		}
		ret.erase(ret.find_last_not_of(sep[0]) + 1, ret.length());
		return ret;
	}

	template<typename R>
	static string Join(R& sets, LPCSTR sep, LPCSTR filler="Null")
	{
		R::iterator it = sets.begin();
		string ret;
		for(; it != sets.end(); it++)
		{
			const string& itval = *it;
			if(itval.length() != 0)
				ret += *it;
			else
				ret += filler;
			ret += sep;
		}
		ret.erase(ret.find_last_not_of(sep[0]) + 1, ret.length());
		return ret;
	}

	static bool InSet(const char* str, STRINGSET& sets);
	static void DumpSet(STRINGSET& sets);
	static string IntToXString(int n)
	{
		char szDest[64] = {0};
		_snprintf_s(szDest, 63, "0x%x", n);
		return szDest;
	}
};

class CFileUtil
{
public:
	CFileUtil();
	~CFileUtil();

public:
	bool Open(LPCTSTR lpszFileName, bool bReadOnly = true);
	void Close(); 
	LONGLONG GetFileSize();
	int Read(LPBYTE lpData, int nSize);
	int Write(LPBYTE lpData, int nSize);

	static DWORD ReadFile(LPCTSTR lpszFileName, char** ppData);

protected:
	HANDLE m_hFile;
};

}
